#!/usr/bin/env python3

# Copyright (c) 2020-2021 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
import enum
from .._util import WinFile
from .._const import Arch
from ._spec import Spec


class AnswerFile(WinFile):

    """
    deploy.chm and ref.chm contain the documentation for unattended answer file.
    The tool setupmgr.exe in the deploy.cab can be used to create a basic unattended answer file. 
    """

    def __init__(self, ts):
        self._ts = ts

        # the following options and default values are from:
        #   1. setupmgr.exe and Ref.chm (it is contained in winxp installation CD)
        #   2. nLite

        self.productKey = None
        self.installationDirectory = "C:\\WINDOWS"
        self.programFilesPath = "C:\\Program Files"
        self.commonProgramFilesPath = "C:\\Program Files\\Common"

        self.bTurnOffFirewall = False
        self.bSkipOOBE = False
        self.bTurnOffHibernate = False

        self.systemRestoreServiceEnabled = True
        self.systemRestoreServiceDiskSpaceThreshold = 0.12
        self.systemRestoreServiceDaysToKeepRestorePoint = 30

        self.users = [
            AnswerFileUser("Administrator", "Administrators", True, False),
            AnswerFileUser("Guest", "Users", False, True),
        ]
        self.autologonUser = None
        self.autologonCount = 0         # 0 for infinite

        self.computerName = None
        self.computerFullName = None
        self.organization = None
        self.workgroup = None           # workgroup and domainName are mutually exclusive
        self.domainName = None

        self.language = ""
        self.bUseLanguageType = True
        self.locale = None
        self.keyboardLayout = None
        self.location = None
        self.languageGroups = []
        self.timezoneIndex = "085"      # GMT Standard Time

        # Network settings
        # Desktop themes

        self.autoUpdate = AnswerFileAutoUpdate()

        self.validClorDepthValues = [
            "Default",
            "16 bit",           # High Color
            "24 bit",           # True Color
            "32 bit",           # True Color
        ]
        self.colorDepth = "Default"

        self.screenResolution = "Default"               # or (width, height)

        self.refreshRate = "Default"                    # or [50-300] Hz

        self.bInstallIis = False            # Internet Information Service
        self.bInstallScw = False            # Security Configuration Wizard

        self.bShowTour = True

    def get_filename(self):
        return "WINNT.SIF"

    def get_content(self):
        spec = Spec(self._ts.arch)
        guiRunOnceBatchFile = GuiRunOnceBatchFile()
        buf = ""

        # assign values
        self.productKey = self._ts.product_key

        if self.bUseLanguageType:
            self.language = self._ts.lang
            self.locale = spec.getLanguageIdByLang(self.language)
            self.location = spec.getLocationByLang(self.language)

        self.languageGroups = [spec.getLanguageGroupCodeByLang(self.language)]

        self.timezoneIndex = spec.getTimezoneCodeByLang(self.language)

        self.autoUpdate.setAutoUpdateMode(self.autoUpdate.Mode.DISABLED)    # winxp is too old, no update available anymore

        self.bShowTour = False                                              # fully unattended

        # generate
        buf += "[Data]\n"
        buf += "AutoPartition=1\n"                  # use an existing partition
        buf += "MsDosInitiated=0\n"                 # use cdrom for installing, must be 0
        buf += "UnattendedInstall=Yes\n"
        buf += self.autoUpdate.generate()
        buf += "\n"

        buf += "[Unattended]\n"
        buf += "UnattendMode=FullUnattended\n"                      # FIXME:  If UnattendMode = FullUnattended, then Setup does not install hardware drivers unless they are properly signed?
        buf += "UnattendSwitch=Yes\n"                               # skip welcome screen
        buf += "OemSkipEula=Yes\n"                                  # skip eula screen
        buf += "OemPreinstall=No\n"                                 # copy files in /$OEM$ from CD-ROM to harddisk, it affects the function of setup driver floppy, so we don't use it, we copy files into harddisk manually
        if True:
            plist = []
            if "virtio" in self._ts.extra_drivers:
                if self._ts.arch == Arch.X86:
                    plist += ["Balloon", "NetKVM", "qxl", "vioserial", "viostor"]
                elif self._ts.arch == Arch.X86_64:
                    plist += ["Balloon", "NetKVM", "vioserial", "viostor"]
                else:
                    assert False
            buf += "OemPnPDriversPath=%s\n" % (";".join(plist))
        # buf += "ComputerType=\n"
        buf += "Repartition=No\n"
        buf += "WaitForReboot=No\n"
        # NoWaitAfterTextMode=1
        # NoWaitAfterGUIMode=1
        buf += "DriverSigningPolicy=Ignore\n"
        buf += "NonDriverSigningPolicy=Ignore\n"
        buf += "TargetPath=%s\n" % (self.installationDirectory.replace("C:", ""))
        if self.keyboardLayout is not None:
            buf += "KeyboardLayout=%s\n"            # windows xp can auto detect
        buf += "Hibernation=No\n"
        if False:
            buf += "AutoActivate=No\n"
            buf += "ActivateProxy=\n"
        buf += "\n"

        # [SystemRestore]
        # DisableSR=1
        # CreateFirstRunRp=0

        buf += "[GuiUnattended]\n"
        buf += "AdminPassword=*\n"
        buf += "EncryptedAdminPassword=No\n"
        buf += "TimeZone=%s\n" % (self.timezoneIndex)
        buf += "OEMSkipRegional=1\n"
        buf += "OemSkipWelcome=1\n"
        # AutoLogon=Yes
        buf += "\n"

        buf += "[UserData]\n"
        buf += "ProductKey=%s\n" % (self.productKey)
        buf += "ComputerName=*\n"
        buf += "FullName=*\n"
        buf += "OrgName=*\n"
        # buf += "ProgramFilesDir=%s\n"
        # buf += "CommonProgramFilesDir=%s\n"
        buf += "\n"

        buf += "[Display]\n"
        buf += "Xresolution=%d\n" % (1024)
        buf += "Yresolution=%d\n" % (768)
        buf += "\n"

        buf += "[RegionalSettings]\n"
        buf += "LanguageGroup=%s\n" % (self.languageGroups[0])
        buf += "Language=%s\n" % (self.locale)
        buf += "\n"

        buf += "[Networking]\n"
        buf += "InstallDefaultComponents=Yes\n"
        buf += "\n"

        # [WindowsFirewall]
        # Profiles=WindowsFirewall.TurnOffFirewall

        # [WindowsFirewall.TurnOffFirewall]
        # Mode=0

        buf += "[GuiRunOnce]\n"
        buf += 'Command1="C:\\%s"\n' % (guiRunOnceBatchFile.get_filename())
        #buf += 'Command1="msiexec.exe /i D:\\guest-agent\\qemu-ga-i386.msi /qn"\n'
        buf += "\n"

        # rundll32.exe setupapi,InstallHinfSection DefaultInstall 132 d:\dir1\driver1.inf
        # rundll32.exe setupapi,InstallHinfSection DefaultInstall 132 d:\dir2\driver2.inf

        # buf += "[TapiLocation]\n"
        # buf += "CountryCode=%s\n" % ("86")
        # buf += "AreaCode=%s\n" % ("00")
        # buf += "Dialing=%s\n" % ("Tone")
        # buf += "\n"

        # buf += "[Identification]\n"
        # buf += "JoinWorkgroup=WORKGROUP\n"
        # buf += "\n"

        # [IEPopupBlocker]
        # BlockPopups="No"
        # FilterLevel="Low"
        # ShowInformationBar="No"

        return buf


class AnswerFileUser:

    def __init__(self, userName, localGroup, bActive, bBlankPassword):
        assert userName is not None
        assert localGroup is not None

        self.userName = userName
        self.fullName = None
        self.description = None

        self.bUnmask = False                    # ?
        self.bBlankPassword = bBlankPassword
        self.bMustHavePassword = False
        self.password = None

        self.bActive = bActive

        self.localGroup = localGroup

        self.passwordExpirationEnabled = True
        self.passwordExpirationDays = 0         # 0 for never


class AnswerFileAutoUpdate:

    class Mode(enum.Enum):
        DOWNLOAD_AND_INSTALL = "Download and install"
        DOWNLOAD_AND_NOTIFY = "Download and notify installation"
        NOTIFY_BEFORE_DOWNLOAD = "Notify before download"
        DISABLED = "Disabled"

    def __init__(self):
        self.autoUpdateMode = self.Mode.DISABLED
        self.autoUpdateElevateNonAdmin = None
        self.autoUpdateIncludeMinorUpdates = None
        self.autoUpdateScheduledDay = None           # week day
        self.autoUpdateScheduledTime = None          # number of hour of a day [0-23]

        # set to default value
        self.setAutoUpdateMode(self.Mode.DOWNLOAD_AND_NOTIFY)

    def setAutoUpdateMode(self, mode):
        assert isinstance(mode, self.Mode)
        self.autoUpdateMode = mode
        if self.autoUpdateMode == self.Mode.DISABLED:
            self.autoUpdateElevateNonAdmin = None
            self.autoUpdateIncludeMinorUpdates = None
            self.autoUpdateScheduledDay = None
            self.autoUpdateScheduledTime = None
        else:
            # set to default value
            self.autoUpdateElevateNonAdmin = False
            self.autoUpdateIncludeMinorUpdates = False
            self.autoUpdateScheduledDay = "Thursday"
            self.autoUpdateScheduledTime = 15

    def generate(self):
        assert self.autoUpdateMode == self.Mode.DISABLED
        return "AutomaticUpdates=No\n"


class OemDirectory:

    def __init__(self, dirpath):
        assert os.path.basename(dirpath) == "$OEM$"
        self._dir = dirpath

    def has_cmdlines_file(self):
        return os.path.exists(os.path.join(self._dir, "cmdlines.txt"))

    def has_dir(self, dirname):
        return os.path.exists(self._getDir(dirname))

    def gat_dir(self, dirname):
        assert self.has_dir(dirname)
        return self._getDir(dirname)

    def _getDir(self, dirname):
        # files in the following directories are copied
        # during text-mode setup when OemPreinstall=Yes
        if dirname == "$1":                             # $OEM$\$1\abc     -> C:\abc
            return os.path.join(self._dir, "$1")
        elif dirname == "$$":                           # $OEM$\$$\abc     -> C:\WINDOWS\abc
            return os.path.join(self._dir, "$$")
        elif dirname == "$Docs":                        # $OEM$\$Docs\abc  -> C:\Document and Settings\abc
            return os.path.join(self._dir, "$Docs")
        elif dirname == "$Progs":                       # $OEM$\$Progs\abc -> C:\Program Files\abc
            return os.path.join(self._dir, "$Progs")
        else:
            assert False


class CmdlinesFile(WinFile):

    """
    The last phase of GUI-mode setup (at the poisition "remain 13 minutes") looks for a file cmdlines.txt in the $OEM$ directory and runs the programs specified with in it.
    The syntax for cmdlines.txt is:
        [Commands]
        "command_1"
        "command_2"
        ...
        "command_x"

    Where each line is such as "command_1" is a command to execute. These commands run with no logged on user and no network. Any user specific information is written to
    the default user and inherited by all users of the system.
    The easiest approach here is to create a batch file to run all the commands, since there is no way to use a quoted "" string in a command in cmdlines.txt.
    Commands in cmdlines.txt take $OEM$ directory as working directory. For other locations, you can either use explicit drive letter in the path or executes commands in
    the system32 directory.
    At the point the commands are being run, the environment variables are not available. 
    """

    def __init__(self, commands):
        self._cmdList = commands

    def get_filename(self):
        return "CMDLINES.TXT"

    def get_content(self):
        buf = "[COMMANDS]\n"
        for cmd in self._cmdList:
            buf += '"%s"\n' % (cmd)
        return buf


class GuiRunOnceBatchFile(WinFile):

    def __init__(self):
        self._cmdList = []
    
    def get_filename(self):
        return "RUNONCE.BAT"

    def add_command(self, command):
        self._cmdList.append(command)

    def get_content(self):
        buf = ""
        for cmd in self._cmdList:
            buf += "%s\n" % (cmd)
        return buf
